using System;
using System.Collections.Generic;
using System.Text;
using DslFactory.Utilities.DteCodeModel;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Modeling;
using System.Net.Security;

namespace Dslfactory.wcfLanguage
{
 internal static class DataClassIncrementalCodeGeneration
 {
  /// <summary>
  /// Subscribe to Interface code generation events
  /// </summary>
  /// <param name="Store"></param>
  /// <param name="eventManagerDirectory"></param>
  public static void SubsribeToStoreEvents(Store Store)
  {
   EventManagerDirectory eventManagerDirectory = Store.EventManagerDirectory;

   // When a property of the DataMember / OperationContract changes, the [OperationContract] attribute must be updated in code
   eventManagerDirectory.ElementPropertyChanged.Add(Store.DomainDataDirectory.GetDomainClass(typeof(FieldOrProperty)),
                                                               new EventHandler<ElementPropertyChangedEventArgs>(EnsureProperties));

  }


  /// <summary>
  /// Ensures the field/property properties of which changed is updated
  /// </summary>
  private static void EnsureProperties(object sender, ElementPropertyChangedEventArgs arg)
  {
   FieldOrProperty member = arg.ModelElement as FieldOrProperty;


   // Rename
   if (arg.DomainProperty.Id == FieldOrProperty.NameDomainPropertyId)
   {
    // Refactor rename
    string oldName = arg.OldValue as string;
    string newName = arg.NewValue as string;
    CodeElement2 element = FindMember(member, oldName) as CodeElement2;
    if (element != null)
     element.RenameSymbol(arg.NewValue as string);
   }

   // Retype
   else if (arg.DomainProperty.Id == FieldOrProperty.MemberFullTypeNameDomainPropertyId)
   {
    CodeElement element = FindMember(member, member.Name);
    if (element != null)
     IncrementalGenerator.EnsureMemberType(element, member.MemberFullTypeName);
   }

   // Change kind
   else if (arg.DomainProperty.Id == FieldOrProperty.KindDomainPropertyId)
   {
    CodeElement element = FindMember(member, member.Name);
    if ((element is CodeVariable) && (member.Kind != DataMemberKind.Field))
    {
     CodeClass2 codeClass = (element as CodeVariable).Parent as CodeClass2;
     IncrementalGenerator.RemoveOrCommentDataMember(element);
     CodeProperty p = IncrementalGenerator.EnsureProperty(codeClass, member.Name, member.MemberFullTypeName);
     vsCMAccess access = GetAccessFromVisibility(member.Visibility);
     if (p.Access != access)
      p.Access = access;
    }
    else if ((element is CodeProperty) && (member.Kind != DataMemberKind.Property))
     {
      CodeClass2 codeClass = (element as CodeProperty).Parent as CodeClass2;
      IncrementalGenerator.RemoveOrCommentDataMember(element);
      CodeVariable v = IncrementalGenerator.EnsureField(codeClass, member.Name, member.MemberFullTypeName);
      vsCMAccess access = GetAccessFromVisibility(member.Visibility);
      if (v.Access != access)
       v.Access = access;
     }
   }

   // Visibility
   else if (arg.DomainProperty.Id == FieldOrProperty.VisibilityDomainPropertyId)
   {
    CodeElement element = FindMember(member, member.Name);
    if (element is CodeVariable)
    {
     vsCMAccess access = GetAccessFromVisibility(member.Visibility);
     CodeVariable v = element as CodeVariable;
     if (v.Access != access)
      v.Access = access;
    }
    else if (element is CodeProperty)
    {
     vsCMAccess access = GetAccessFromVisibility(member.Visibility);
     CodeProperty p = element as CodeProperty;
     if (p.Access != access)
      p.Access = access;
    }
   }

  }


  /// <summary>
  /// Find the member of given name
  /// </summary>
  /// <param name="name"></param>
  /// <returns></returns>
  private static CodeElement FindMember(FieldOrProperty member, string name)
  {
   KnownCodeTypes knownCodeTypes = KnownCodeTypes.FromStore(member.Store);
   if (knownCodeTypes == null)
    return null;
   CodeClass2 codeClass = knownCodeTypes.GetFullNamedType(member.DataClass.FullName) as CodeClass2;
   if (codeClass == null)
    return null;
   CodeElement element = IncrementalGenerator.FindFirstMember(codeClass, name);
   return element;
  }


  /// <summary>
  /// Generate the members
  /// </summary>
  /// <param name="codeClass"></param>
  /// <param name="dataClass"></param>
  public static void GenerateMembers(CodeClass2 codeClass, DataClass dataClass)
  {
   foreach (FieldOrProperty member in dataClass.Members)
    if (member.Kind == DataMemberKind.Field)
    {
     CodeVariable v = IncrementalGenerator.EnsureField(codeClass, member.Name, member.MemberFullTypeName);
     if (v != null)
     {
      vsCMAccess access = GetAccessFromVisibility(member.Visibility);
      if (v.Access != access)
       v.Access = access;
     }
    }
    else if (member.Kind == DataMemberKind.Property)
    {
     CodeProperty property = IncrementalGenerator.EnsureProperty(codeClass, member.Name, member.MemberFullTypeName);
     if (property != null)
     {
      vsCMAccess access = GetAccessFromVisibility(member.Visibility);
      if (property.Access != access)
       property.Access = access;
     }
    }
  }


  /// <summary>
  /// Transform the Visibility value into an Access constant
  /// </summary>
  /// <param name="visibility"></param>
  /// <returns></returns>
  public static vsCMAccess GetAccessFromVisibility(Visibility visibility)
  {
   switch (visibility)
   {
    case Visibility.Public:
     return vsCMAccess.vsCMAccessPublic;

    case Visibility.Protected:
     return vsCMAccess.vsCMAccessProtected;

    case Visibility.Private:
    return vsCMAccess.vsCMAccessPrivate;

    case Visibility.Internal:
     return vsCMAccess.vsCMAccessAssemblyOrFamily;

    default:
     return vsCMAccess.vsCMAccessPrivate;
   }
  }
 }
}
